﻿//    Copyright 2014 LuoZhihui
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//      http://www.apache.org/licenses/LICENSE-2.0
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.

#region  导入名称空间

using System;
using System.Collections.Generic;
using System.Linq;
using LotteryEngine.Infrastructure.Querying;
using LotteryEngine.Infrastructure.UnitOfWork;
using LotteryEngine.Model;
using LotteryEngine.Model.Repositories;
using LotteryEngine.Model.Services;
using LotteryEngine.Services.Interfaces;

#endregion

namespace LotteryEngine.Services.Impl
{
	public class LotteryEngine : ILotteryEngine
	{
		private readonly IConfigRepository _configRepository;
		private readonly ConfigService _configService;
		private readonly IPrizeRepository _prizeRepository;
		private readonly IRecordRepository _recordRepository;
		private readonly IUnitOfWork _unitOfWork;


		public LotteryEngine(
			ISweepstakeRepository sweepstakeRepository,
			IPrizeRepository prizeRepository,
			IConfigRepository configRepository,
			IRecordRepository recordRepository,
			IUnitOfWork unitOfWork)
		{
			_prizeRepository = prizeRepository;
			_configRepository = configRepository;
			_recordRepository = recordRepository;
			_unitOfWork = unitOfWork;
			_configService = new ConfigService(sweepstakeRepository, prizeRepository, configRepository, unitOfWork);
		}


		/// <summary>
		///     随机码
		/// </summary>
		private string RandomSerial
		{
			get
			{
				var seed = GenerateRandomSeed();
				return Math.Abs(seed).ToString().PadRight(10, '0');
			}
		}

		/// <summary>
		///     生成无重复的随机数种子
		/// </summary>
		public int GenerateRandomSeed()
		{
			var tick = Guid.NewGuid().GetHashCode();
			var seed = (int) (tick & 0xffffffffL) | tick >> 32;

			return seed;
		}


		/// <summary>
		///     基于TPLine 的转轮选择
		/// </summary>
		/// <param name="balancedConfigs">奖项配置列表</param>
		/// <param name="stopedPosition">模拟轮盘转轮运动停止后，指针指向的位置</param>
		private static Config BasedTpLineWheelChoice(List<Config> balancedConfigs, double stopedPosition)
		{
			var choiced = balancedConfigs.Find(x => x.IsByeItem);

			for (var i = 0; i < balancedConfigs.Count; i++)
			{
				var startPosition = ProbabilitySegmentLength(balancedConfigs, i);
				var endPosition = ProbabilitySegmentLength(balancedConfigs, i + 1);

				if (!(startPosition < stopedPosition) || !(stopedPosition < endPosition))
				{
					continue;
				}

				choiced = balancedConfigs[i];
				break;
			}

			return choiced;
		}

		/// <summary>
		///     计算奖项中奖概率之和
		/// </summary>
		/// <param name="lotteryCfglist"></param>
		/// <returns></returns>
		private static double SumProbabilities(IEnumerable<Config> lotteryCfglist)
		{
			return lotteryCfglist.Sum(item => item.Probability);
		}

		/// <summary>
		///     模拟转动轮盘
		/// </summary>
		/// <param name="balancedConfigs">平衡的奖项概率配置列表</param>
		private Config SimulatingRotationRoulette(List<Config> balancedConfigs)
		{
			var probabilities = SumProbabilities(balancedConfigs);
			var seed = GenerateRandomSeed();
			// 用随机数模拟轮盘停止位置
			var simulatingStopedPosition = new Random(seed).NextDouble()*probabilities;
			var playResult = BasedTpLineWheelChoice(balancedConfigs, simulatingStopedPosition);

			return playResult;
		}

		/// <summary>
		///     验证是有效抽奖结果
		/// </summary>
		/// <param name="choicedConfig"></param>
		/// <returns></returns>
		private static bool IsValidResult(Config choicedConfig)
		{
			if (choicedConfig.IsByeItem) return false;
			if (choicedConfig.Prize.PrizeBlance <= 0) return false;

			return choicedConfig.PrizeBlance > 0;
		}


		/// <summary>
		///     计算概率段长度
		/// </summary>
		/// <param name="balancedConfigs">奖项配置列表</param>
		/// <param name="toplimit">上限</param>
		/// <returns></returns>
		private static double ProbabilitySegmentLength(IList<Config> balancedConfigs, int toplimit)
		{
			var theTempProbability = 0.00d;

			for (var i = 0; i < toplimit && i < balancedConfigs.Count; i++)
			{
				theTempProbability += balancedConfigs[i].Probability;
			}


			return theTempProbability;
		}

		/// <summary>
		///     开始抽奖
		/// </summary>
		/// <param name="sweepstake"></param>
		/// <param name="player"></param>
		/// <param name="eligibilityVerification">抽奖资格验证 true 可以参与抽奖 false 不能参与抽奖，永远不会中奖</param>
		/// <returns></returns>
		public void StartTheLottery(Sweepstake sweepstake, string player, Func<bool> eligibilityVerification)
		{
			var query = new Query();
			query.Add(Criterion.Create<Config>(config => config.Sweepstake.Id, sweepstake.Id, CriteriaOperator.Eq));

			var lotteryConfigs = _configRepository.FindBy(query);

			var balancedConfigs = _configService.AutomaticBalanceTotalProbability(lotteryConfigs);

			var lotteryDoNotWin = balancedConfigs.Find(x => x.IsByeItem);
			var playRecord = new Record();
			playRecord.Sweepstake = sweepstake;
			playRecord.PlaySerial = RandomSerial;
			playRecord.PlayTime = DateTime.Now;
			playRecord.Player = player;
			playRecord.PlayResult = false;
			playRecord.Prize = lotteryDoNotWin.Prize;
			if (sweepstake.IsUpdating || !eligibilityVerification())
			{
				_recordRepository.Add(playRecord);
			}
			else
			{
				//模拟转动轮盘
				var playResult = SimulatingRotationRoulette(balancedConfigs);
				if (!IsValidResult(playResult))
				{
					_recordRepository.Add(playRecord);
				}
				playRecord.PlayResult = true;
				playRecord.Prize = playResult.Prize;
				playResult.PrizeBlance -= 1;
				playResult.Prize.PrizeBlance -= 1;

				_recordRepository.Add(playRecord);
				_prizeRepository.Save(playResult.Prize);
				_configRepository.Save(playResult);
			}
			Console.WriteLine(playRecord.ToString());
			_unitOfWork.Commit();
		}
	}
}